import React, {
  ComponentType,
  FC,
  MouseEventHandler,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react'
import { DataUri, Graph, Node } from '@antv/x6'
import { IGraph } from '@/components/MainGraph/IMainGraph'
import lang from './MainGraph.i18n.json'
import { observer } from 'mobx-react'
import {
  connectingOptions,
  edgeInteractionHandler,
  graphPlugInInit,
  highlightingOptions,
  interactingOptions,
  mousewheelOptions,
  panningOptions,
} from '@/components/MainGraph/GraphOptions'
import { useLang } from '@/hooks/i18n'
import {
  useAppStore,
  useChatStore,
  useGraphStore,
  useMetaStore,
  useWtsStore,
} from '@/hooks'
import { useColorVar, useStyles } from '@/hooks/styles'
import {
  addAssetInCanvas,
  addMethodInCanvas,
  switchLanguage,
  addEdgeInCanvas,
} from '@/components/MainGraph/utils'
import throttle from 'lodash/debounce'
import debounce from 'lodash/debounce'
import { Button, FloatButton, message, notification, Select } from 'antd'
import ReactDOM from 'react-dom/client'
import styles from './MainGraph.module.scss'
import { BorderInnerOutlined, QuestionCircleOutlined } from '@ant-design/icons'
import { PortTooltip } from '@/components/MainGraph/PortTooltip'
import { TextAsset } from '@/components/graph/assets/TextAsset/TextAsset'
import { ICardContext, UUID } from '@/store/metaStore'
import { RequirementsSummaryMethod } from '@/components/graph/methods/RequirementsSummaryMethod/RequirementsSummaryMethod'
import { v4 as uuidv4 } from 'uuid'
import { Export } from '@antv/x6-plugin-export'
import { Message } from '@arco-design/web-react'
import { upload_asset } from 'wts/dist/api/AssetService'

type TmpCardInfoDisabled = { enabled: false }
type TmpCardInfoEnabled = {
  enabled: true
  cardID: UUID
  position: { x: number; y: number } // SYNC with antv
}
type TmpCardInfo = TmpCardInfoEnabled | TmpCardInfoDisabled

export const MainGraph: FC<IGraph> = observer(({ projectID }) => {
  const { t } = useLang(lang, 'MainGraph')
  const c = useColorVar()
  const styleClass = useStyles(styles)
  const refContainer = useRef()
  const refGraph = useRef()
  const refMiniMap = useRef()
  const appStore = useAppStore()
  const graphStore = useGraphStore()
  const metaStore = useMetaStore()
  const wtsStore = useWtsStore()
  const chatStore = useChatStore()
  const [messageApi, contextHolder1] = message.useMessage()
  const [api, contextHolder2] = notification.useNotification()

  const [graphSize, setGraphSize] = useState<[number, number]>([0, 0])
  const [graphTranslate, setGraphTranslate] = useState<[number, number]>([0, 0])
  const [graphScale, setGraphScale] = useState<number>(1)
  const [tmpCardInfo, setTmpCardInfo] = useState<TmpCardInfo>({
    enabled: false,
  })

  const autoSave = async () => {
    //  dataUri: b64 encoded image data
    graphStore.graph.toPNG(
      async (dataUri) => {
        DataUri.downloadDataUri(dataUri, 'test.png')
        // console.log(dataUri)
        // const file = new File([dataURItoBlob(dataUri)], 'test.png')
        // const res = await upload_asset('test', 'image', file)
        // console.log('autosave', res)
      },
      { width: 1000, height: 1000, quality: 2 }
    )
    Message.info('自动保存成功')
  }

  const handleMouseMove = (position: [number, number]) => {
    if (!tmpCardInfo.enabled) {
      return
    }
    setTmpCardInfo({
      ...tmpCardInfo,
      position: graphStore.graph.clientToLocal(...position),
    })
  }

  const confirmCard = () => {
    if (tmpCardInfo.enabled) {
      setTmpCardInfo({ enabled: false })
    }
  }

  // TODO: make it general
  const addTextAssetCard = () => {
    const cardID = uuidv4()
    const position = [0, 0] as [number, number] // TODO: use coord of button
    metaStore.addTextAssetCard(graphStore.graph)(true, cardID, position)
    setTmpCardInfo({
      enabled: true,
      cardID,
      position: { x: position[0], y: position[1] },
    })
  }

  const addImageAssetCard = () => {
    const cardID = uuidv4()
    const position = [0, 0] as [number, number] // TODO: use coord of button
    metaStore.addImageAssetCard(graphStore.graph)(true, cardID, position)
    setTmpCardInfo({
      enabled: true,
      cardID,
      position: { x: position[0], y: position[1] },
    })
  }

  useEffect(() => {
    // TODO：初始化应放在何处？
    // wts初始化
    wtsStore
      .initOperator(projectID)
      .then((_) => {
        metaStore.load(wtsStore, graphStore.graph)
        return
      })
      .catch((err) => console.error('maingraph init', err))
    // metaStore.load(wtsStore, graphStore.graph)
  }, [graphStore.graph, metaStore, wtsStore])

  useEffect(() => {
    if (!tmpCardInfo.enabled) {
      return
    }
    graphStore.graph
      .getNodes()
      .filter((node) => node.id === tmpCardInfo.cardID)[0]
      ?.setPosition(tmpCardInfo.position)
  }, [tmpCardInfo])

  // 窗口size监听
  const handleContainerResizeDebounce = useCallback(
    debounce(
      (width, height) => {
        graphStore.graph.resize(width, height)
        // graphStore.graph.centerContent()
      },
      200,
      { leading: false, trailing: true, maxWait: 1000 }
    ),
    []
  )

  const selectedCellsUpdateThrottle = useCallback(
    throttle(
      (graph: Graph) => {
        graphStore.selectedCellUpdate(graph.getSelectedCells())
      },
      100,
      { leading: false, trailing: true, maxWait: 1000 }
    ),
    []
  )

  useEffect(() => {
    const containerDom = refContainer.current as HTMLElement
    graphStore.graph = new Graph({
      container: refGraph.current,
      grid: true, // 栅格点
      width: containerDom.clientWidth,
      height: containerDom.clientHeight,
      background: {
        color: c('white'),
      },
      virtual: true,
      scaling: {
        min: 0.05, // 最小缩放比例,
        max: 5, // 最大缩放比例
      },
      /* panning: {
        ...panningOptions, // CTRL/ALT 平移
      }, */
      mousewheel: {
        ...mousewheelOptions, // CTRL/ALT 缩放
      },
      connecting: {
        ...connectingOptions, // 连线的约束规则
      },
      highlighting: {
        ...highlightingOptions, // 连接桩连线时交互高亮
      },
      interacting: {
        ...interactingOptions(graphStore),
      },
      onPortRendered(args) {
        const selectors = args.contentSelectors
        const container = selectors && selectors.foContent
        const portGroup = args.port.group as
          | 'in'
          | 'out'
          | 'absolute_in'
          | 'absolute_out'
        const tip =
          portGroup === 'in' || portGroup === 'absolute_in' ? 'Input' : 'Output'
        if (container) {
          ReactDOM.createRoot(container as HTMLElement).render(
            <PortTooltip portGroup={portGroup} tip={tip} />
          )
        }
      },
    })
    // 插件初始化
    graphPlugInInit(
      graphStore.graph,
      graphStore,
      messageApi,
      refMiniMap.current
    )
    graphStore.graph.use(new Export())
    // （插件初始化补丁1）框选监听
    graphStore.graph.on('selection:changed', () => {
      selectedCellsUpdateThrottle(graphStore.graph)
    })
    // （插件初始化补丁2）画布size、scale、translate监听，后续确认后改至GraphOption
    {
      graphStore.graph.on('resize', ({ width, height }) =>
        setGraphSize([width, height])
      )
      graphStore.graph.on('scale', ({ sx }) => setGraphScale(sx))
      graphStore.graph.on('translate', ({ tx, ty }) =>
        setGraphTranslate([Number(tx.toFixed(1)), Number(ty.toFixed(1))])
      )
      graphStore.graph.on('edge:connected', ({ isNew, edge }) => {
        edgeInteractionHandler(isNew, edge, messageApi, metaStore)
      })
      graphStore.graph.on('node:moved', ({ x, y, node }) => {
        const { id } = node
        console.log('moved', x, y, node.position())
        metaStore.cardContext[id].position = [
          node.position().x,
          node.position().y,
        ] // TODO: type annotation
      })
    }

    // graphStore.graph.centerContent()
    return () => {
      // 销毁
      graphStore.graph.dispose()
    }
  }, [])

  useEffect(() => {
    const containerDom = refContainer.current as HTMLElement
    const resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
        const { width, height } = entry.contentRect
        handleContainerResizeDebounce(width, height)
      }
    })

    resizeObserver.observe(containerDom)

    return () => {
      resizeObserver.unobserve(containerDom)
    }
  }, [])

  useEffect(() => {
    api.open({
      message: t`Operate guidebook`,
      description: (
        <div {...styleClass(['instruction-container'])}>
          <div {...styleClass(['instruction-row'])}>
            {`CTRL/ALT + ${t`Left-Pressed`}： ${t`Translation`}`}
          </div>
          <div
            {...styleClass(['instruction-row'])}
          >{`CTRL/ALT + ${t`Mouse Wheel`}：${t`Scaling`}`}</div>
          <div {...styleClass(['instruction-row'])}>
            {`${t`Left-Pressed`} + ${t`Drag`}：${t`Selection`}`}
          </div>
        </div>
      ),
      duration: 12,
      placement: 'bottomLeft',
    })
  }, [])

  const init = () => {
    metaStore.addTextAssetCard(graphStore.graph)(true, uuidv4(), [32, 200], '')

    wtsStore.operator
      .add_method('generate_design_goals')
      .then((designMethod) => {
        metaStore.addRequirementSummaryMethod(graphStore.graph)(
          true,
          designMethod.id,
          [500, 200]
        )
        return
      })
      .catch((exception) => {
        console.error(exception)
      })
  }

  return (
    <div
      ref={refContainer}
      {...styleClass(['main-graph-wrapper'])}
      onMouseMove={(e) => handleMouseMove([e.clientX, e.clientY])}
      onClick={confirmCard}
    >
      {/* <MyHeader /> */}
      {contextHolder1}
      {contextHolder2}
      <FloatButton
        {...styleClass(['float-button-1'])}
        icon={<QuestionCircleOutlined rev={undefined} />}
        type="default"
      />
      <FloatButton
        {...styleClass(['float-button-2'])}
        icon={<BorderInnerOutlined rev={undefined} />}
        type="default"
        onClick={() => {
          graphStore.graph.scale(1, 1)
          graphStore.graph.centerContent()
        }}
      />

      <div {...styleClass(['scale-tip-wrapper'])}>
        <Select
          defaultValue={'English'}
          style={{ width: 96 }}
          options={[
            { value: 'English', label: 'English' },
            { value: '中文', label: '中文' },
          ]}
          onChange={(value) =>
            switchLanguage(
              appStore,
              graphStore.graph,
              value as 'English' | '中文'
            )
          }
        />
        <span
          {...styleClass(['scale-tip-text'])}
        >{`size: [ ${graphSize[0]}, ${graphSize[1]} ] -- scale: ${graphScale} -- translate: [ ${graphTranslate[0]}, ${graphTranslate[1]} ]`}</span>
        <Button onClick={init}>init</Button>
        <Button onClick={addTextAssetCard}>add text asset</Button>
        <Button onClick={addImageAssetCard}>add image asset</Button>
        <Button onClick={() => metaStore.save(wtsStore)}>save</Button>
        <Button onClick={() => metaStore.load(wtsStore, graphStore.graph)}>
          load
        </Button>
        <Button onClick={() => graphStore.graph.clearCells()}>clear</Button>
        <Button onClick={autoSave}>test auto save</Button>
        {/* TODO: use t18n */}
      </div>
      <div ref={refGraph} {...styleClass(['main-graph-layout'])}></div>
      <div ref={refMiniMap} {...styleClass(['mini-map-wrapper'])}></div>
    </div>
  )
})
